package gov.fnal.xpath2ldap.util;

import org.w3c.dom.Document;

import java.io.*;
import javax.xml.namespace.QName;
import javax.xml.stream.*;
import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.EndElement;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.dom.DOMSource;

public class Merger {

    private static final QName prodName = new QName("product");
    private static final QName pidName = new QName("dn");
    protected static XMLInputFactory xml_input_factory;// = XMLInputFactory.newInstance();

    public static void main(String[] args)
            throws FileNotFoundException, XMLStreamException {

        // Use  the reference implementation for the  XML input factory
        System.setProperty(
                "javax.xml.stream.XMLInputFactory",
                "com.bea.xml.stream.MXParserFactory");
        // Create the XML input factory
        XMLInputFactory factory = XMLInputFactory.newInstance();
        // Create XML event reader 1
        XMLEventReader r1 =
                factory.createXMLEventReader(new FileReader("/home/tdh/testing/cache/bdii1.xml"));
        // Create XML event reader 2
        XMLEventReader r2 =
                factory.createXMLEventReader(new FileReader("/home/tdh/testing/cache/bdii2.xml"));

        // Create the output factory
        XMLOutputFactory xmlof = XMLOutputFactory.newInstance();
        // Create XML event writer
        XMLEventWriter xmlw = xmlof.createXMLEventWriter(System.out);

        // Read to first <product> element in document 1
        // and output to result document
        //StartElement pid1 = readToNextElement(r1, xmlw, false);
        // Read to first <product> element in document 1
        // without writing to result document
        //StartElement pid2 = readToNextElement(r2, null, false);

        XMLEventReader descender = r1;
        XMLEventReader waiter = r2;
        XMLEventReader hold;
        XMLEvent evdesc =  readToNextElement(descender, xmlw, false);
        XMLEvent evwait =  readToNextElement(waiter, xmlw, false);
        System.out.println("Descer:\t" + descender);
        System.out.println("Waiter:\t" + waiter);
        System.out.println();
        int desctype;
        int waittype;
        String descname;
        String waitname;
        boolean descending=false;
        // Loop over both XML input streams
        while (evdesc != null || evwait != null) {
            if(evdesc==null) {
                evwait = readToNextElement(waiter, null, false);
                if(evwait==null) continue;
                waittype = evwait.getEventType();
                if(waittype==XMLEvent.START_ELEMENT) {
                    waitname = evwait.asStartElement().getName().getLocalPart();
                    System.out.println("Waiter:\t" + waiter + "\t" + evwait.asStartElement());
                } else if (waittype==XMLEvent.END_ELEMENT) {
                    waitname = evwait.asEndElement().getName().getLocalPart();
                    System.out.println("Waiter:\t" + waiter + "\t" + evwait.asEndElement());
                }
                continue;
            }
            if(evwait==null) {
                evdesc = readToNextElement(descender, null, false);
                if(evdesc==null) continue;
                desctype = evdesc.getEventType();
                if(desctype==XMLEvent.START_ELEMENT) {
                    descname = evdesc.asStartElement().getName().getLocalPart();
                    System.out.println("Descer:\t" + descender + "\t" + evdesc.asStartElement());
                } else if (desctype==XMLEvent.END_ELEMENT) {
                    descname = evdesc.asEndElement().getName().getLocalPart();
                    System.out.println("Descer:\t" + descender + "\t" + evdesc.asEndElement());
                }
            }

            desctype = evdesc.getEventType();
            waittype = evwait.getEventType();

            if(waittype==XMLEvent.START_ELEMENT) {
                waitname = evwait.asStartElement().getName().getLocalPart();
                System.out.println("0Waiter:\t" + waiter + "\t" + evwait.asStartElement());
                if(desctype==XMLEvent.START_ELEMENT) {
                    descname = evdesc.asStartElement().getName().getLocalPart();
                    System.out.println("0Descer:\t" + descender + "\t" + evdesc.asStartElement());
                    if(!descname.equals(waitname)) {
                        hold = descender; descender = waiter; waiter = hold;
                        evwait = evdesc;
                        descending=true;
                    }
                    //evdesc = (XMLEvent) descender.next();
                    evdesc = readToNextElement(descender, null, false);
                } else if (desctype==XMLEvent.END_ELEMENT) {
                    descname = evdesc.asEndElement().getName().getLocalPart();
                    System.out.println("1Descer:\t" + descender + "\t" + evdesc.asEndElement());
                    if(descname.equals(waitname)) {
                        hold = descender; descender = waiter; waiter = hold;
                        evwait = evdesc;
                        descending=false;
                    }
                    evdesc = readToNextElement(descender, null, false);
                }
            } else if (waittype==XMLEvent.END_ELEMENT) {
                waitname = evwait.asEndElement().getName().getLocalPart();
                System.out.println("0waiter:\t" + waiter + "\t" + evwait.asEndElement());
                if(desctype==XMLEvent.END_ELEMENT) {
                    descname = evdesc.asEndElement().getName().getLocalPart();
                    System.out.println("0descer:\t" + descender + "\t" + evdesc.asEndElement());
                    if(descname.equals(waitname)) {
                        hold = descender; descender = waiter; waiter = hold;
                        evwait = evdesc;
                    }
                    evdesc = readToNextElement(descender, null, false);
                } else if (desctype==XMLEvent.START_ELEMENT) {
                    descname = evdesc.asStartElement().getName().getLocalPart();
                    System.out.println("1descer:\t" + descender + "\t" + evdesc.asStartElement());
                    //if(!descname.equals(waitname)) {
                    //    hold = descender; descender = waiter; waiter = hold;
                    //    evwait = evdesc;
                    //}
                    evdesc = readToNextElement(descender, null, false);
                }
            }


        }
        xmlw.close();
    }


    /**
     * @param reader - the document reader
     * @param writer - the document writer
     * @param processEnd - forces the document end to be written
     * @return - the next merge criterion value
     * @throws XMLStreamException
     */
    private static XMLEvent readToNextElement(XMLEventReader reader,
                                            XMLEventWriter writer, boolean processEnd) throws XMLStreamException {
        // Nesting level
        int level = 0;
        while (true) {
            // Read event to be written to result document
            XMLEvent event = (XMLEvent) reader.next();
            // Avoid double processing of document end
            //if (!processEnd)
            if (false)
                switch (event.getEventType()) {
                    case XMLEvent.START_ELEMENT :
                        ++level;
                        break;
                    case XMLEvent.END_ELEMENT :
                        if (--level < 0)
                            return null;
                        break;
                }
            // Output event
            //if (writer != null)
            //    writer.add(event);
            // Look at next event
            //event = reader.peek();
            switch (event.getEventType()) {
                case XMLEvent.START_ELEMENT :
                    // Start element - stop at <product> element
                    //QName name = event.asStartElement().getName();
                    //if (name.equals(prodName)) {
                        //return event
                        //        .asStartElement()
                        //        .getAttributeByName(pidName)
                        //        .getValue();
                    //}
                    //break;
                    //String name = event.asStartElement().getName().getLocalPart();
                     return event;
                     //return event.asStartElement().getAttributes(). .getAttributeByName(pidName).getValue();
                case XMLEvent.END_ELEMENT :
                     return event;
                case XMLEvent.END_DOCUMENT :
                    // Stop at end of document
                    return null;
            }
        }
    }

    private static String readToNextEvent(XMLEventReader reader,
                                            XMLEventWriter writer, boolean processEnd) throws XMLStreamException {
        XMLEvent event = reader.nextEvent();
        if (writer != null) {
            writer.add(event);
            writer.flush();
        }
        return event.toString();

    }
}
